/*********************************************************************
* File:: WaypointAnalyser.h
* \brief The waypoint file inspection tool.
*********************************************************************/
#pragma once
#pragma warning (disable: 4482) // Ignore non-standard extension warnings

#include "../../agents/Agent.h"
using namespace agents;
#include "../../agents/interfaces/ICollisionHandler.h"
using namespace agents::interfaces;

#include <list>
using namespace std;


namespace util{ namespace tools{ 
	class WA_Results;
	class WA_CollisionHandler;
	class WA_UnreachablesState;
	class WA_ConnectionsState;

	/**
	* \class WaypointAnalyser
	* \brief A tool to inspect waypoint configuration files.
	*
	* The agent responds to the following chat messages:
	* - \c fov: Reports the field-of-view
	*
	* \author Instituto Superior Tecnico de Lisboa, Portugal
	* \author Technical University of Lisbon, Portugal
	* 
	* \author Agentes Autonomos e Sistemas Multiagente
	* \author Autonomous Agents and Multiagent Systems
	* 
	* \version	1.0
	* \date		2006/2007
	**/
	class WaypointAnalyser: public Agent{
	public:
		WaypointAnalyser(edict_t *enity, int team, int model);
		~WaypointAnalyser();

		WA_CollisionHandler* collisionHandler;
		double roundEndTime;

	private:
		void newRound();
		void think();
		void handleMessage(ICommunication::MessageRecord& record);

		int findDeadends();
		int findUnreachables(int startIndex);
		void findUnreachables(int currentIndex, list<int>* closed);
		void stopValidations();
		void findAndValidateUnreachables(int startIndex, int endIndex);
		void validateConnections(int startIndex, int endIndex);

		WA_UnreachablesState* _validateUnreachables;
		WA_ConnectionsState* _validateConnections;
	};

	typedef struct WA_Connection_s
	{
		int waypoint;
		int neighbor;
	} WA_Connection;

	/**
	* \class WA_Results
	**/
	class WA_Results
	{
	public:
		void toFile(const char* fileNameSuffix, const char* itemName, list<int>* waypoints);
		void toFile(const char* fileNameSuffix, const char* noItemsName, const char* yesItemsName, 
			list<int>* noWaypoints, list<int>* yesWaypoints);
		void toFile(const char* fileNameSuffix, const char* noItemName, const char* yesItemName, 
			list<WA_Connection*>* noConnections, list<WA_Connection*>* yesConnections);
		void write(const char* str);
		void write(const char* name, list<int>* waypoints);
		void write(list<WA_Connection*>*);
		void openFile(const char* suffix);
		void closeFile();

	private:
		FILE* _file;
	};

	/**
	* \class WA_CollisionHandler
	**/
	class WA_CollisionHandler: public ICollisionHandler {
	public:
		WA_CollisionHandler(WaypointAnalyser* owner);

		void newRound();
		bool isStuck(){ return _owner->getNavigation().isStuck(); }
		bool detectCollision();
		void handleCollision(){ _owner->getNavigation().handleCollision(); }
		// Bug 05 - Reset collision state - Start
		void resetCollisionState(){ _owner->getNavigation().resetCollisionState(); }
		// Bug 05 - End

	private:
		WaypointAnalyser* _owner;
		double _nextCollisionDetectionReportTime;
	};

	/**
	* \class WA_UnreachablesState
	**/
	class WA_UnreachablesState: public IWaypointHitHandler
	{
	public:
		WA_UnreachablesState(WaypointAnalyser* owner);
		void newRound();
		void start(int startIndex, int endIndex);
		void stop();
		void update();
		void waypointHit(int index);

	private:
		void pushReachable(int index);

		WaypointAnalyser* _owner;
		bool _isOn;
		int _startIndex;
		int _currentIndex;
		int _endIndex;
		list<int> _unreachables;
		list<int> _reachables;
		bool _hits[MAX_WAYPOINTS];
	};

	class WA_ConnectionsState: public IWaypointHitHandler
	{
	public:
		typedef enum States_e{
			end,
			selectWaypoint,
			selectNeighbor,
			moveToNeighbor,
			moveToWaypoint, 
			reporting,
			pending,
		} States;

		WA_ConnectionsState(WaypointAnalyser* owner);
		void newRound();
		void start(int startIndex, int endIndex);
		void stop();
		void update();
		void report();
		void waypointHit(int index);

	private:
		void pushConnection(int startIndex, int endIndex);
		void write(const char* msg);

		WaypointAnalyser* _owner;
		list<WA_Connection*> _invalidConnections;
		list<WA_Connection*> _validConnections;
		States _state;
		States _pendingState;
		int _startIndex;
		int _endIndex;

		bool _hits[MAX_WAYPOINTS][MAX_WAYPOINTS];
		int _startConnectionIndex;

		int _currentIndex;
		Waypoint* _currentWaypoint;
		int _currentNeighborIndex;
		int _currentNeighborWaypointIndex;
		int _pathLength;

		WA_Results _results;
	};
}}